﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Windows;
using System.Windows.Media;

using PetriNetSharp.Engine;
using PetriNetSharp.Converter;
using System.Diagnostics;

namespace PetriNetSharp
{
	/// <summary>
	/// Delegate for when an arc is finished
	/// </summary>
	/// <param name="finishedArc">The finished arc</param>
	public delegate void ArcFinishedEvent(Arc finishedArc);

	/// <summary>
	/// Delegate for when an arc is cancelled
	/// </summary>
	/// <param name="message">The reason for why the arc is being cancelled</param>
	public delegate void ArcCancelledEvent(string message);

	/// <summary>
	/// Factory for helping create arcs
	/// </summary>
	public class ArcFactory
	{
		#region events

		/// <summary>
		/// This event is fired when that arc that is currently being
		/// created is finished
		/// </summary>
		public event ArcFinishedEvent ArcFinished;

		/// <summary>
		/// This event is fired when the arc that is currently being 
		/// created is cancelled or Stopped
		/// </summary>
		public event ArcCancelledEvent ArcCancelled;


		/// <summary>
		/// This event is raised when the starting PetriNetObject
		/// is added
		/// </summary>
		public event Action StartingPetriNetObjectAdded;

		#endregion

		/// <summary>
		/// The line that will represent the arc
		/// </summary>
		private Line lineArc;

		/// <summary>
		/// Whether or not an arc is currently being created
		/// </summary>
		private bool isArcBeingCreated;

		/// <summary>
		/// The arc that is being constructed
		/// </summary>
		private Arc arc;

		/// <summary>
		/// The staring point of the arc
		/// </summary>
		private PetriObject start;

		/// <summary>
		/// The ending point of the arc
		/// </summary>
		private PetriObject end;

		/// <summary>
		/// Create a new ArcFactory
		/// </summary>
		public ArcFactory()
		{
			StopArcCreation();
		}

		/// <summary>
		/// Get whether the arc has a starting petri net object
		/// </summary>
		public bool ArcHasStart { get { return start != null; } }

		/// <summary>
		/// Get or set whether an arc is currently being created
		/// </summary>
		public bool IsArcBeingCreated
		{
			get { return isArcBeingCreated; }
			set
			{
				if (isArcBeingCreated != value)
				{
					if (value)
						StartNewArc();
					else
						StopArcCreation();
				}
			}
		}

		/// <summary>
		/// Get the line that will represent the arc
		/// </summary>
		/// <preconditions>
		/// The ArcFactory must currently be in the process of creating an Arc
		/// </preconditions>
		public Line ArcLine
		{
			get
			{
				#region preconditions
				Debug.Assert(IsArcBeingCreated, 
					"Cannot get Arc Line when no arc is currently being created",
					"Get ArcLine was called when IsArcBeingCreated was false");
				#endregion

				return lineArc;
			}
		}

		/// <summary>
		/// Add a petri object to the Arc being created
		/// </summary>
		/// <param name="petri">The petri object</param>
		/// <preconditions>
		/// ArcBeingCreated must be true
		/// </preconditions>
		public void AddPetriObject(PetriObject petri)
		{
			#region preconditions
			Debug.Assert(IsArcBeingCreated, 
				"Cannot add petri object to arc when no arc is being created", 
				"AddPetriObject was called when IsArcBeingCreated was false");
			#endregion

			if (start == null)
			{
				start = petri;
				lineArc.X1 = start.Location.X;
				lineArc.Y1 = start.Location.Y;

				if (StartingPetriNetObjectAdded != null)
				{
					StartingPetriNetObjectAdded();
				}
			}
			else if (start.GetType().Equals(petri.GetType()))	//Start and end cannot be the same type
			{
				CancelArc("Start and End cannot be the same type for an Arc");
			}
			else
			{
				end = petri;
				lineArc.X2 = end.Location.X;
				lineArc.Y2 = end.Location.Y;

				FinishArc();
			}
		}


		/// <summary>
		/// Finishes the creation of the arc and starts the creation
		/// of a new arc
		/// </summary>
		private void FinishArc()
		{
			


			//Make sure this arc does not already exist
			bool alreadyExists;
			if (start is Transition) 
			    alreadyExists = ((Transition)start).IsConnectedToPlace((Place)end);
			else 
			    alreadyExists = ((Transition)end).IsConnectedToPlace((Place)start);

			//If the arc already exists, cancel the arc
			if (alreadyExists)
			{
				CancelArc("That place and tranisition are already connected");
				return;
			}

			arc = new Arc(start, end);
			
			if (ArcFinished != null)
				ArcFinished(arc);

			StartNewArc();
		}


		#region Start, Stop, Cancel

		/// <summary>
		/// Reset the factory to start creation of an new arc
		/// </summary>
		public void StartNewArc()
		{
			isArcBeingCreated = true;
			lineArc = new Line();
			lineArc.Stroke = (Brush)lineArc.FindResource("ArcCreationBrush");
			lineArc.StrokeThickness = PetriNetSharp.Controller.ArcController.ARC_STROKE_THICKNESS;
			arc = null;
			start = end = null;
			
		}
		/// <summary>
		/// Stop the current arc's creation
		/// </summary>
		public void StopArcCreation()
		{
			
			if (ArcCancelled != null)
				ArcCancelled("");

			isArcBeingCreated = false;
		}

		/// <summary>
		/// Cancel the current arcs creation
		/// </summary>
		/// <param name="message">The message as to why the cancellation is happening</param>
		public void CancelArc(String message)
		{
			if (ArcCancelled != null)
				ArcCancelled(message);

			StartNewArc();
		}

		#endregion
	}
}
